import { Component } from 'cc'
import BTComposite from '../Base/BTComposite'
import BTConditional from '../Base/BTConditional'
import BTNode from '../Base/BTNode'
import BTParent from '../Base/BTParent'
import BTTree from '../Base/BTTree'
import Tree1 from '../Biz/Tree/Tree1'
import { NodeStatus } from '../Enum/NodeStatus'

export class BehaviorMgr extends Component {
  restartWhenComplete: boolean = true
  tree: BTTree
  nodeList: Array<BTNode> = []
  activeStack: Array<number> = []
  parentIndex: Array<number> = []
  childrenIndex: Array<Array<number>> = []

  relativeChildIndex: Array<number> = []
  parentCompositeIndex: Array<number> = []
  childConditionalIndex: Array<Array<number>> = []

  start() {
    this.enableBehavior()
  }

  restart() {
    this.pushNode(0)
  }

  enableBehavior() {
    this.tree = new Tree1()
    this.parentIndex.push(-1)
    this.relativeChildIndex.push(-1)
    this.parentCompositeIndex.push(-1)
    this.addToNodeList(this.tree.root, { parentCompositeIndex: -1 })
    this.pushNode(0)
  }

  addToNodeList(node: BTNode, data: { parentCompositeIndex: number }) {
    this.nodeList.push(node)
    const index = this.nodeList.length - 1
    if (node instanceof BTParent) {
      this.childrenIndex.push([])
      for (let i = 0; i < node.childrens.length; i++) {
        this.parentIndex.push(index)
        this.relativeChildIndex.push(i)
        this.childrenIndex[index].push(this.nodeList.length)
        if (node instanceof BTComposite) {
          data.parentCompositeIndex = index
        }
        this.parentCompositeIndex.push(data.parentCompositeIndex)
        this.addToNodeList(node.childrens[i], data)
      }
    } else {
      this.childrenIndex.push(null)
      this.childConditionalIndex.push(null)
      if (node instanceof BTConditional) {
        const parentCompositeIndex = this.parentCompositeIndex[index]
        if (parentCompositeIndex !== -1) {
          this.childConditionalIndex[parentCompositeIndex].push(index)
        }
      }
    }
  }

  update() {}

  tick() {
    let prevIndex = -1
    let prevStatus = NodeStatus.Inactive
    while (this.activeStack.length) {
      const curIndex = this.activeStack[this.activeStack.length - 1]
      if (curIndex === prevIndex) break
      prevIndex = curIndex
      prevStatus = this.runNode(curIndex, prevStatus)
    }
  }

  runNode(index: number, prevStatus: NodeStatus) {
    this.pushNode(index)
    const node = this.nodeList[index]
    let status = prevStatus
    if (node instanceof BTParent) {
      status = this.runParentNode(index, status)
    } else {
      status = node.onUpdate()
    }
    if (status !== NodeStatus.Running) {
      this.popNode(index, status)
    }
    return status
  }

  runParentNode(index: number, status: NodeStatus) {
    const node = this.nodeList[index] as BTParent
    let childStatus = NodeStatus.Inactive
    while (node.canExecute() && childStatus !== NodeStatus.Running) {
      childStatus = status = this.runNode(this.childrenIndex[index][node.index], status)
    }
    return status
  }

  pushNode(index: number) {
    if (this.activeStack.length === 0 || this.activeStack[this.activeStack.length-1] !== index) {
      this.activeStack.push(index)
      const node = this.nodeList[index]
      node.onStart()
    }
  }

  popNode(index: number, status: NodeStatus) {
    this.activeStack.pop()
    const node = this.nodeList[index]
    node.onEnd()
    const parentIndex = this.parentIndex[index]
    if (parentIndex !== -1) {
      const parentNode = this.nodeList[parentIndex] as BTParent
      parentNode.onChildExecuted(status)
    }
    if (this.restartWhenComplete && !this.activeStack.length) {
      this.restart()
    }
  }
}
